package com.xcity.game.combat.builder;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.game.combat.Combat;
import com.xcity.game.combat.Combat.Type;
import com.xcity.game.combat.CombatBuilder;
import com.xcity.game.combat.CombatTeam;
import com.xcity.game.combat.impl.TimeLimitedCombat;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.combat.unit.CombatUnit;
import com.xcity.game.combat.unit.Combatable;
import com.xcity.game.combat.unit.impl.EmployeeCombatUnit;
import com.xcity.game.combat.unit.impl.EmptyCombatUnit;
import com.xcity.game.combat.unit.impl.NpcCombatUnit;
import com.xcity.game.combat.unit.impl.PlayerCombatUnit;
import com.xcity.game.formation.Formation;
import com.xcity.game.npc.NpcCombatable;

public class DefaultCombatBuilder implements CombatBuilder {

	private Combat.Type type;
	private CombatListener listener;
	
	private CombatTeam srcTeam;
	private Combatable srcLeader;
	private List<Combatable> srcMembers;
	
	private CombatTeam dstTeam;
	private Combatable dstLeader;
	private List<Combatable> dstMembers;
	
	public DefaultCombatBuilder() {}
	
	public DefaultCombatBuilder setType(Combat.Type type) {
		this.type = type;
		return this;
	}
	
	public DefaultCombatBuilder setListener(CombatListener listener) {
		this.listener = listener;
		return this;
	}
	
	public DefaultCombatBuilder setSourceLeader(Combatable c) {
		this.srcLeader = c;
		return this;
	}
	
	public DefaultCombatBuilder setDestLeader(Combatable c) {
		this.dstLeader = c;
		return this;
	}
	
	public DefaultCombatBuilder addSourceMembers(List<? extends Combatable> members) {
		if (this.srcMembers == null) {
			this.srcMembers = new ArrayList<Combatable>(members);
		} else {
			this.srcMembers.addAll(members);
		}
		return this;
	}
	
	public DefaultCombatBuilder addDestMembers(List<? extends Combatable> members) {
		if (this.dstMembers == null) {
			this.dstMembers = new ArrayList<Combatable>(members);
		} else {
			this.dstMembers.addAll(members);
		}
		return this;
	}
	
	public DefaultCombatBuilder addSourceMember(Combatable c) {
		if (this.srcMembers == null) {
			this.srcMembers = new ArrayList<Combatable>(Formation.FORMATION_CAPCITY);
		}
		this.srcMembers.add(c);
		return this;
	}
	
	public DefaultCombatBuilder addDestMember(Combatable c) {
		if (this.dstMembers == null) {
			this.dstMembers = new ArrayList<Combatable>(Formation.FORMATION_CAPCITY);
		}
		this.dstMembers.add(c);
		return this;
	}
	
	public CombatTeam getSourceTeam() {
		return srcTeam != null ? srcTeam : (srcTeam = new CombatTeam());
	}
	
	public CombatTeam getDestTeam() {
		return dstTeam != null ? dstTeam : (dstTeam = new CombatTeam());
	}
	
	@Override
	public Combat build() {
		CombatTeam src = buildTeam(getSourceTeam(), srcLeader, srcMembers);
		CombatTeam dst = buildTeam(getDestTeam(), dstLeader, dstMembers);
		return new Combat(src, dst, type, listener);
	}
	
	@Override
	public Combat build(Class<? extends Combat> clazz) {
		CombatTeam src = buildTeam(getSourceTeam(), srcLeader, srcMembers);
		CombatTeam dst = buildTeam(getDestTeam(), dstLeader, dstMembers);
		try {
			Constructor<? extends Combat> con = constructors.get(clazz);
			if (con == null) {
				con = clazz.getConstructor(CombatTeam.class, CombatTeam.class, Type.class, CombatListener.class);
				constructors.put(clazz, con);
			}
			return con.newInstance(src, dst, type, listener);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Combat(src, dst, type, listener);
	}

	static CombatTeam buildTeam(CombatTeam team, Combatable leader, List<Combatable> members) {
		// add the leader in the first position
		team.add(toCombatUnit(team, leader));
		// add the members
		for (int i = 0, size = members != null ? members.size() : 0; i < size; i++) {
			Combatable c = members.get(i);
			team.add(toCombatUnit(team, c));
		}
		// 填充剩余空位
		int n = CombatTeam.DEFAULT_TEAM_CAPACITY - team.size();
		for (int i = 0; i < n; i++) {
			team.add(EmptyCombatUnit.INSTANCE);
		}
		return team;
	}
	
	static CombatUnit toCombatUnit(CombatTeam team, Combatable c) {
		if (c == null) {
			return EmptyCombatUnit.INSTANCE;
		}
		if (c instanceof CombatUnit) {
			return (CombatUnit) c;
		}
		if (c instanceof Employee) {
			return new EmployeeCombatUnit(team, (Employee) c);
		}
		if (c instanceof NpcCombatable) {
			return new NpcCombatUnit(team, ((NpcCombatable) c).toTemplate());
		}
		if (c instanceof Player) {
			return new PlayerCombatUnit(team, (Player) c);
		}
		return EmptyCombatUnit.INSTANCE;
	}
	
	static Map<Class<? extends Combat>, Constructor<? extends Combat>> constructors = new HashMap<>(2);
	static {
		try {
			Constructor<? extends Combat> con = TimeLimitedCombat.class.getConstructor(CombatTeam.class, CombatTeam.class, Type.class, CombatListener.class);
			constructors.put(TimeLimitedCombat.class, con);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}
